<!doctype html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <script>
    if (window.customElements) {
      customElements.forcePolyfill = true;
    }
    window.ShadyDOM = {
      force: true
    };
  </script>
  <script src="../../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
  <script src="wct-browser-config.js"></script>
  <script src="../../node_modules/wct-browser-legacy/browser.js"></script>
  <script type="module">
    import {setSyncInitialRender} from '../../lib/utils/settings.js';
    window.syncInitialRender = Boolean(window.location.search.match('syncInitialRender'));
    setSyncInitialRender(window.syncInitialRender);
  </script>
  <script type="module" src="../../polymer-legacy.js"></script>
</head>
<body>

<dom-module id="x-project">
  <template strip-whitespace>x-project: [<slot></slot>]</template>
</dom-module>
<script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-project'
});
</script>

<dom-module id="x-reproject">
  <template strip-whitespace>
    <x-project>x-reproject: [<slot></slot>]</x-project>
  </template>
</dom-module>
<script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-reproject'
});
</script>

<dom-module id='x-rereproject'>
  <template strip-whitespace>
    <x-reproject>x-rereproject: [<slot></slot>]</x-reproject>
  </template>
</dom-module>
<script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-rereproject',
  attachedCount: 0,
  attached: function() {
    this.attachedCount++;
  }
});
</script>

<dom-module id="x-test">
  <template strip-whitespace>
    <x-rereproject><span id="projected">projected</span></x-rereproject>
  </template>
</dom-module>
<script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-test'
});
</script>

<dom-module id="x-test-no-distribute">
  <template>
    <span>Local dom without insertion point.</span>
  </template>
</dom-module>
<script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-test-no-distribute'
});
</script>

<dom-module id="x-distribute">
  <template>
    <div>
      <span>Elements without test attribute</span>
      <div id="notTestContainer" style="color: white; background-color: green; min-height: 1em;">
        <slot id="notTestContent"></slot>
      </div>
      <span>Elements with test attribute</span>
      <div style="color: white; background-color: red; min-height: 1em;">
        <div id="testContainer">
          <slot id="testContent" name="test"></slot>
        </div>
      </div>
    </div>
  </template>
</dom-module>

<script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: "x-distribute"
});
</script>

<dom-module id="x-compose">
  <template><x-project id="project"></x-project></template>
</dom-module>
<script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-compose'
});
</script>


<dom-module id="x-select3">
  <template><slot id="x-select3-slot" name="s3"></slot></template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-select3'});
</script>
</dom-module>

<dom-module id="x-select2">
  <template><x-select3 id="select"><slot id="x-select2-slot" name="s2"></slot></x-select3></template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-select2'});
</script>
</dom-module>

<dom-module id="x-select1">
  <template><x-select2 id="select"><slot id="x-select1-slot" name="s1"></slot></x-select2></template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-select1'});
</script>
</dom-module>

<dom-module id="x-echo">
  <template><slot></slot></template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-echo'});
</script>
</dom-module>

<dom-module id="x-simple">
  <template><div>simple</div></template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-simple'});
</script>
</dom-module>


<dom-module id="x-redistribute-a-b">
  <template>
    <x-echo id="echo1"><slot name="a"></slot></x-echo>
    <x-echo id="echo2"><slot name="b"></slot></x-echo>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-redistribute-a-b'});
</script>
</dom-module>

<dom-module id="x-attr">
  <template>Attr1</template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-attr',
  hostAttributes: {
    slot: 'bar'
  }

});
</script>
</dom-module>

<dom-module id="x-attr2">
  <template>Attr2</template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-attr2',
  // TODO(sorvell): cannot put `slot` into properties because it is patched.
  ready: function() {
    this.slot = 'foo';
  }

});
</script>
</dom-module>

<dom-module id="x-select-attr">
  <template>
    Foo: [<slot name="foo"></slot>]
    Bar: [<slot name="bar"></slot>]
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-select-attr'});
</script>
</dom-module>

<dom-module id="x-compose-select-attr">
  <template>
    <x-select-attr id="select">
      <x-attr id="attr1"></x-attr>
      <x-attr2 id="attr2"></x-attr2>
    </x-select-attr>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-compose-select-attr'});
</script>
</dom-module>


<dom-module id="x-clonate">
  <template><span>[</span><slot></slot><span>]</span></template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-clonate'
});
</script>
</dom-module>

<dom-module id="x-attach3">
  <template>
    <style>
      :host {
        display: block;
        border: 1px dashed orange;
        padding: 4px;
        box-sizing: border-box;
      }

      :host > ::slotted(.add3) {
        box-sizing: border-box;
        height: 20px;
        background: #333;
        border: 2px solid yellow;
      }
    </style>
    <slot></slot>
    <template is="dom-if" if="{{shouldIf(done.count)}}">
      <x-attach2></x-attach2>
    </template>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-attach3',
  properties: {
    done: {value: {count: 0}}
  },
  ready: function() {
    this.done.count++;
  },
  attached: function() {
    var d = document.createElement('div');
    d.className = 'add3';
    this.appendChild(d);
  },
  shouldIf: function(x) {
    return x < 3;
  }
});
</script>
</dom-module>

<dom-module id="x-attach2">
  <template>
    <style>
      :host {
        display: block;
        border: 1px dashed tomato;
        padding: 4px;
      }

      x-attach3 > ::slotted(.add2) {
        box-sizing: border-box;
        height: 20px;
        background: gray;
        border: 2px solid yellow;
      }
    </style>
    <x-attach3><slot></slot></x-attach3>
    <template is="dom-if" if="{{shouldIf(done.count)}}">
      <x-attach1></x-attach1>
    </template>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-attach2',
  properties: {
    done: {value: {count: 0}}
  },
  ready: function() {
    this.done.count++;
  },
  attached: function() {
    var d = document.createElement('div');
    d.className = 'add2';
    this.appendChild(d);
  },
  shouldIf: function(x) {
    return x < 3;
  }
});
</script>
</dom-module>

<dom-module id="x-attach1">
  <template>
    <style>
      :host {
        display: block;
        border: 1px dashed seagreen;
        padding: 4px;
      }

      .slotContainer ::slotted(.add1) {
        box-sizing: border-box;
        height: 20px;
        background: lightgray;
        border: 2px solid yellow;
      }
    </style>
    <x-attach2><div class="slotContainer"><slot></slot><div></x-attach2>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-attach1',
  attached: function() {
    var d = document.createElement('div');
    d.className = 'add1';
    this.appendChild(d);
  }
});
</script>
</dom-module>

<dom-module id="x-commented">
  <template><span>[</span><!--comment--><slot></slot></span><span>]</span></slot></template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({is: 'x-commented'});
</script>
</dom-module>


<dom-module id="polymer-dom-repeat">
  <template>
    <div>
      <div>
        <div>
          <div id="container">
            <template id="repeater" is="dom-repeat" items="{{items}}">
              <div>stuff</div>
            </template>
          </div>
        </div>
      </div>
    </div>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'polymer-dom-repeat',
  properties: {
    items: {
      value: function() {
        return ['a', 'b', 'c', 'd', 'e'];
      }
    }
  }
});
</script>
</dom-module>

<dom-module id="x-deep-contains">
  <template>
    <div id="shadowed"></div>
    <slot name="light"></slot>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-deep-contains',
  created: function() {
    var e = document.createElement('div');
    e.setAttribute('slot', 'light');
    this.appendChild(e);
    e = document.createElement('div');
    e.setAttribute('slot', 'notdistributed');
    this.appendChild(e);
  }
});
</script>
</dom-module>

<dom-module id="x-wrapped">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-wrapped'
});
</script>
</dom-module>


<dom-module id="x-shadow-host-root-0-0">
  <template>
    <slot></slot>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-0-0',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-0-0-light-0">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-0-0-light-0',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-0-0-light">
  <template>
    <div>
      <div>
        <x-shadow-host-root-0-0-light-0></x-shadow-host-root-0-0-light-0>
      </div>
    </div>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-0-0-light',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-0-1">
  <template>
    <slot></slot>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-0-1',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-0-1-light">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-0-1-light',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-0">
  <template>
    <slot></slot>
    <div>
      <x-shadow-host-root-0-0>
        <x-shadow-host-root-0-0-light></x-shadow-host-root-0-0-light>
      </x-shadow-host-root-0-0>
    </div>
    <x-shadow-host-root-0-1>
      <x-shadow-host-root-0-1-light></x-shadow-host-root-0-1-light>
    </x-shadow-host-root-0-1>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-0',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-0-light">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-0-light',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-1-0">
  <template>
    <slot></slot>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-1-0',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-1-0-light">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-1-0-light',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-1-1">
  <template>
    <slot></slot>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-1-1',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-1-1-light">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-1-1-light',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-1">
  <template>
    <slot></slot>
    <div>
      <x-shadow-host-root-1-0>
        <x-shadow-host-root-1-0-light></x-shadow-host-root-1-0-light>
      </x-shadow-host-root-1-0>
    </div>
    <div>
      <div>
        <div>
          <x-shadow-host-root-1-1>
            <x-shadow-host-root-1-1-light></x-shadow-host-root-1-1-light>
          </x-shadow-host-root-1-1>
        </div>
      </div>
    </div>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-1',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-1-light-0">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-1-light-0',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-1-light">
  <template>
    <x-shadow-host-root-1-light-0></x-shadow-host-root-1-light-0>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-1-light',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root">
  <template>
    <slot></slot>
    <div>
      <div>
        <x-shadow-host-root-0>
          <x-shadow-host-root-0-light></x-shadow-host-root-0-light>
        </x-shadow-host-root-0>
      </div>
    </div>
    <div>
      <x-shadow-host-root-1>
        <x-shadow-host-root-1-light></x-shadow-host-root-1-light>
      </x-shadow-host-root-1>
    </div>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-shadow-host-root-light">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-shadow-host-root-light',
  hostAttributes: {
    tabindex: '-1'
  }
});
</script>
</dom-module>

<dom-module id="x-outer">
  <template>
    <x-inner id="inner">
      <slot></slot>
    </x-inner>
  </template>
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-outer'
});
</script>
</dom-module>

<dom-module id="x-inner">
  <script type="module">
import { Polymer } from '../../polymer-legacy.js';
Polymer({
  is: 'x-inner',
  attached: function(){
    this.listen(this, 'click', '_onClick');
  },
  _onClick: function(event){
    this._eventTarget = event.target;
  }
});
</script>
</dom-module>


<x-test></x-test>

<div class="accessors">
  <x-test-no-distribute><div class="child"></div></x-test-no-distribute>
  <x-project><div class="child"></div></x-project>
</div>

<x-test-no-distribute id="noDistribute">
  <div class="bar">Bar</div>
  <div class="foo">Foo</div>
</x-test-no-distribute>

<x-select1>
  <div class="select-child"></div>
</x-select1>

<x-select-class1>
  <div></div>
</x-select-class1>

<x-select-attr>
  <x-attr></x-attr>
</x-select-attr>

<x-compose-select-attr></x-compose-select-attr>

<x-redistribute-a-b></x-redistribute-a-b>

<div id="container">
  <x-echo></x-echo>
  <span>1</span>
  <span>2</span>
</div>

<x-deep-contains></x-deep-contains>

<x-wrapped></x-wrapped>

<x-shadow-host-root>
  <x-shadow-host-root-light></x-shadow-host-root-light>
</x-shadow-host-root>

<x-outer>
  <button>click me</button>
</x-outer>
<script>
  if (!window.ShadyDOM) {
    ShadyDOM = {
      patch: function() {},
      flush: function() {}
    };
  }
</script>
<script type="module">
import { flush } from '../../lib/utils/flush.js';
import { dom } from '../../lib/legacy/polymer.dom.js';

function createEnabledElement(tag) {
  var e = document.createElement(tag);
  document.body.appendChild(e);
  document.body.removeChild(e);
  return e;
}

function allInsertionPoints(e) {
  var r = [];
  while (e) {
    e = e.assignedSlot;
    if (e) {
      r.push(e);
    }
  }
  return r;
}

var checkUnpatchedDom = false;

suite('ShadyDOM', function() {

var testElement;

suiteSetup(function() {
  testElement = document.querySelector('x-test');
});

test('querySelector (local)', function() {
  var projected = testElement.root.querySelector('#projected');
  assert.equal(projected.textContent, 'projected');
  var p2 = testElement.querySelector('#projected');
  assert.isNull(p2);
  var rere = testElement.root.querySelector('x-rereproject');
  assert.equal(rere.is, 'x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  assert.equal(re.is, 'x-reproject');
  var p = re.root.querySelector('x-project');
  assert.equal(p.is, 'x-project');
});

test('querySelectorAll (local)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  var p = re.root.querySelector('x-project');
  var rereList = rere.root.querySelectorAll('*');
  assert.include(rereList, re);
  assert.equal(rereList.length, 2);
  var reList = re.root.querySelectorAll('*');
  assert.include(reList, p);
  assert.equal(reList.length, 2);
  var pList = p.root.querySelectorAll('*');
  assert.equal(pList.length, 1);
});

test('querySelector (light)', function() {
  var projected = testElement.root.querySelector('#projected');
  var rere = testElement.root.querySelector('x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  var p = re.root.querySelector('x-project');
  assert.equal(rere.querySelector('#projected'), projected);
  assert(re.querySelector('slot'));
  assert(p.querySelector('slot'));
});

test('querySelectorAll (light)', function() {
  var projected = testElement.root.querySelector('#projected');
  var rere = testElement.root.querySelector('x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  var p = re.root.querySelector('x-project');
  assert.equal(rere.querySelectorAll('#projected')[0], projected);
  assert(re.querySelectorAll('slot').length, 1);
  assert(p.querySelectorAll('slot').length, 1);
});

test('querySelectorAll with dom-repeat', function() {
  var el = document.createElement('polymer-dom-repeat');
  document.body.appendChild(el);
  el.$.repeater.render();
  flush();
  assert.equal(el.$.container.querySelectorAll('*').length, 7, 'querySelectorAll finds repeated elements');
  document.body.removeChild(el);
});

test('querySelector document', function() {
  assert.ok(document.querySelector('body'));
});

test('projection', function() {
  var projected = testElement.root.querySelector('#projected');
  assert.equal(projected.textContent, 'projected');
  var rere = testElement.root.querySelector('x-rereproject');
  assert.equal(rere.is, 'x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  assert.equal(re.is, 'x-reproject');
  var p = re.root.querySelector('x-project');
  assert.equal(p.is, 'x-project');
  var c1 = rere.root.querySelector('slot');
  assert.include(c1.assignedNodes({flatten: true}), projected);
  var c2 = re.root.querySelector('slot');
  assert.include(c2.assignedNodes({flatten: true}), projected);
  var c3 = p.root.querySelector('slot');
  assert.include(c3.assignedNodes({flatten: true}), projected);
  var ip$ = [c1, c2, c3];
  var as$ = allInsertionPoints(projected);
  assert.deepEqual(as$, ip$);
});

test('shadyRoot (reproject)', function() {
  var select = document.querySelector('x-select1');
  var child = select.firstElementChild;
  var c1 = select.root.querySelector('slot');
  var c2 = select.$.select.root.querySelector('slot');
  var c3 = select.$.select.$.select.root.querySelector('slot');
  assert.equal(c1.getAttribute('name'), 's1');
  assert.equal(c2.getAttribute('name'), 's2');
  assert.equal(c3.getAttribute('name'), 's3');
  // var ip$ = [c1, c2, c3];
  assert.equal(child.className, 'select-child');
  assert.equal(allInsertionPoints(child).length, 0);
  child.slot = 's1';
  flush();
  assert.deepEqual(allInsertionPoints(child), [c1]);
  child.slot = 's2';
  c1.setAttribute('name', 's2');
  c1.slot = 's2';
  flush();
  assert.deepEqual(allInsertionPoints(child), [c1, c2]);
  child.slot = 's3';
  c1.setAttribute('name', 's3');
  c1.slot = 's3';
  c2.setAttribute('name', 's3');
  c2.slot = 's3';
  flush();
  assert.deepEqual(allInsertionPoints(child), [c1, c2, c3]);
  child.slot = '';
  flush();
  assert.deepEqual(allInsertionPoints(child), []);
  child.slot = 's3';
  flush();
  assert.deepEqual(allInsertionPoints(child), [c1, c2, c3]);
  child.slot = '';
  flush();
  assert.deepEqual(allInsertionPoints(child), []);
  child.slot = 's2';
  c1.setAttribute('name', 's2');
  c1.slot = 's2';
  c2.setAttribute('name', 's2');
  c2.slot = 's2';
  flush();
  assert.deepEqual(allInsertionPoints(child), [c1, c2]);
  child.slot = 's1';
  c1.setAttribute('name', 's1');
  c1.slot = 's1';
  flush();
  assert.deepEqual(allInsertionPoints(child), [c1]);
  child.slot = '';
  flush();
  assert.deepEqual(allInsertionPoints(child), []);
});

test('without a host setting hostAttributes/reflecting properties provokes distribution', function() {
  var e = document.querySelector('x-select-attr');
  var ip$ = e.root.querySelectorAll('slot');
  var c = e.firstElementChild;
  flush();
  assert.equal(allInsertionPoints(c)[0], ip$[1], 'child not distributed based on host attribute');
  c.slot = 'foo';
  flush();
  assert.equal(allInsertionPoints(c)[0], ip$[0], 'child not distributed based on reflecting attribute');
  c.slot = '';
  flush();
  assert.equal(allInsertionPoints(c).length, 0, 'child not distributed based on reflecting attribute');
});

test('within a host setting hostAttributes/reflecting properties provokes distribution', function() {
  var e = document.querySelector('x-compose-select-attr');
  var ip$ = e.$.select.root.querySelectorAll('slot');
  var c1 = e.$.attr1;
  flush();
  assert.equal(allInsertionPoints(c1)[0], ip$[1], 'child not distributed based on host attribute');
  c1.slot = 'foo';
  flush();
  assert.equal(allInsertionPoints(c1)[0], ip$[0], 'child not distributed based on reflecting attribute');
  c1.slot = 'bar';
  flush();
  assert.equal(allInsertionPoints(c1)[0], ip$[1], 'child not distributed based on reflecting attribute');
  var c2 = e.$.attr2;
  flush();
  assert.equal(allInsertionPoints(c2)[0], ip$[0], 'child not distributed based on default value');
});

test('appendChild (light)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var s = document.createElement('span');
  s.id = 'added';
  s.textContent = 'Added';
  rere.appendChild(s);
  assert.equal(testElement.root.querySelector('#added'), s);
});

test('insertBefore (light)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var ref = testElement.root.querySelector('#added');
  var s = document.createElement('span');
  s.id = 'added2';
  s.textContent = 'Added2';
  rere.insertBefore(s, ref);
  assert.equal(testElement.root.querySelector('#added2'), s);
});

test('removeChild (light)', function() {
  var added = testElement.root.querySelector('#added');
  var added2 = testElement.root.querySelector('#added2');
  var rere = testElement.root.querySelector('x-rereproject');
  assert.equal(testElement.root.querySelectorAll('*').length, 4);
  rere.removeChild(added);
  rere.removeChild(added2);
  assert.equal(testElement.root.querySelectorAll('*').length, 2);
});

test('appendChild (local)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var s = document.createElement('span');
  s.id = 'local';
  s.textContent = 'Local';
  rere.root.appendChild(s);
  assert.equal(rere.root.querySelector('#local'), s);
});

test('insertBefore (local)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var ref = testElement.root.querySelector('#local');
  var s = document.createElement('span');
  s.id = 'local2';
  s.textContent = 'Local2';
  rere.root.insertBefore(s, ref);
  assert.equal(rere.root.querySelector('#local2'), s);
});

test('removeChild (local)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var local = rere.root.querySelector('#local');
  var local2 = rere.root.querySelector('#local2');
  rere.root.removeChild(local);
  rere.root.removeChild(local2);
  assert.equal(rere.root.querySelectorAll('#local').length, 0);
});

test('localDom.insertBefore first element results in minimal change', function() {
  var children = testElement.root.childNodes;
  var rere = testElement.root.querySelector('x-rereproject');
  assert.equal(rere.attachedCount, 1);
  var s = document.createElement('span');
  s.id = 'local-first';
  s.textContent = 'Local First';
  testElement.root.insertBefore(s, children[0]);
  assert.equal(testElement.root.querySelector('#local-first'), s);
  assert.equal(rere.attachedCount, 1);
  testElement.root.removeChild(s);
  assert.equal(rere.attachedCount, 1);
});

test('appendChild (fragment, local)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var fragment = document.createDocumentFragment();
  var childCount = 5;
  for (var i=0; i < childCount; i++) {
    var s = document.createElement('span');
    s.textContent = i;
    fragment.appendChild(s);
  }
  rere.root.appendChild(fragment);
  var added = rere.root.querySelectorAll('span');
  assert.equal(added.length, childCount);
  for (i=0; i < added.length; i++) {
    rere.root.removeChild(added[i]);
  }
  assert.equal(rere.root.querySelectorAll('span').length, 0);
});

test('insertBefore (fragment, local)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var fragment = document.createDocumentFragment();
  var childCount = 5;
  for (var i=0; i < childCount; i++) {
    var s = document.createElement('span');
    s.textContent = i;
    fragment.appendChild(s);
  }
  var l = document.createElement('span');
  l.textContent = 'last';
  rere.root.appendChild(l);
  rere.root.insertBefore(fragment, l);
  var added = rere.root.querySelectorAll('span');
  assert.equal(added.length, childCount+1);
  assert.equal(added[added.length-1], l);
  for (i=0; i < added.length; i++) {
    rere.root.removeChild(added[i]);
  }
  assert.equal(rere.root.querySelectorAll('span').length, 0);
});

test('mutations using fragments without logical dom', function() {
  var d = document.createElement('div');
  document.body.appendChild(d);
  assert.equal(d.childNodes.length, 0);
  var frag = document.createDocumentFragment();
  var c = document.createElement('div');
  frag.appendChild(c);
  d.appendChild(frag);
  assert.equal(d.childNodes.length, 1);
  assert.equal(d.firstChild, c);
  var c1 = document.createElement('div');
  frag.appendChild(c1);
  d.appendChild(frag);
  assert.equal(d.childNodes.length, 2);
  assert.equal(d.firstChild, c);
  assert.equal(d.lastChild, c1);
});

test('appendChild interacts with unmanaged parent tree', function() {
  var container = document.querySelector('#container');
  var echo = container.firstElementChild;
  assert.equal(echo.localName, 'x-echo');
  var s1 = echo.nextElementSibling;
  assert.equal(s1.textContent, '1');
  var s2 = s1.nextElementSibling;
  assert.equal(s2.textContent, '2');
  assert.equal(container.children.length, 3);
  echo.appendChild(s1);
  flush();
  assert.equal(container.children.length, 2);
  assert.equal(echo.nextElementSibling, s2);
  echo.appendChild(s2);
  flush();
  assert.equal(container.children.length, 1);
  assert.equal(echo.nextElementSibling, null);
  container.appendChild(s1);
  flush();
  assert.equal(container.children.length, 2);
  assert.equal(echo.nextElementSibling, s1);
  container.appendChild(s2);
  flush();
  assert.equal(container.children.length, 3);
  assert.equal(echo.nextElementSibling, s1);
  assert.equal(s1.nextElementSibling, s2);
});

test('distribute (forced)', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  var p = re.root.querySelector('x-project');
  var s = document.createElement('span');
  s.id = 'light';
  s.textContent = 'Light';
  rere.appendChild(s);
  assert.equal(rere.querySelector('#light'), s);
  assert.equal(s.parentNode, rere);
  if (checkUnpatchedDom) {
    assert.notEqual(Polymer.TreeApi.Composed.getParentNode(s), rere);
  }
  if (checkUnpatchedDom) {
    assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p);
  }
  rere.removeChild(s);
  if (checkUnpatchedDom) {
    assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p);
  }
  if (checkUnpatchedDom) {
    assert.equal(Polymer.TreeApi.Composed.getParentNode(s), null);
  }
});

test('queryDistributedElements', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  var p = re.root.querySelector('x-project');
  var projected = testElement.root.querySelector('#projected');
  var d$ = p.queryDistributedElements('*');
  assert.equal(d$.length, 1);
  assert.equal(d$[0], projected);

});

test('getEffectiveChildNodes', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  var projected = testElement.root.querySelector('#projected');
  var c$ = re.getEffectiveChildNodes();
  assert.equal(c$.length, 3);
  assert.equal(c$[1], projected);
});

test('querySelector', function() {
  var test = document.querySelector('x-test');
  var rere = document.querySelector('x-rereproject');
  var projected = document.querySelector('#projected');
  assert.ok(test);
  assert.notOk(rere);
  assert.notOk(projected);
});

test('event', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var re = rere.root.querySelector('x-reproject');
  var p = re.root.querySelector('x-project');
  var eventHandled = 0;
  testElement.addEventListener('test-event', function(e) {
    eventHandled++;
    assert.equal(e.composedPath()[0], p);
    assert.equal(e.target, testElement);
    var path = e.composedPath();
    // path includes window only on more recent Shadow DOM implementations
    // account for that here.
    assert.ok(path.length >= 10);
    assert.equal(path[0], p);
    assert.equal(path[2], re);
    assert.equal(path[4], rere);
    assert.equal(path[6], testElement);
    // event.path *should* be an array
    assert.isArray(path);
    assert.isFunction(path.indexOf);
    assert(path.indexOf(testElement) > -1);
  });

  rere.addEventListener('test-event', function(e) {
    eventHandled++;
    assert.equal(e.target, rere);
  });

  p.fire('test-event');
  assert.equal(eventHandled, 2);
});

test('event.target', function() {
  var e = document.querySelector('x-outer');
  var b = document.querySelector('x-outer button');
  b.dispatchEvent(new Event('click', {bubbles: true}));
  assert.equal(e.$.inner._eventTarget.localName, 'button', 'event restarted incorrectly');
});

test('parentNode', function() {
  var rere = testElement.root.querySelector('x-rereproject');
  var projected = testElement.root.querySelector('#projected');
  assert.equal(testElement.parentNode, document.body);
  assert.equal(projected.parentNode, rere);
});

test('childNodes is an array', function() {
  var test = document.querySelector('x-test');
  assert.isTrue(Array.isArray(test.childNodes));
});

test('cloneNode shallow', function() {
  var a = document.createElement('div');
  a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
  var b = a.firstElementChild.cloneNode();
  document.body.appendChild(b);
  assert.equal(b.childNodes.length, 0, 'shallow copy has incorrect children');
  if (checkUnpatchedDom) {
    assert.equal(b.children.length, 2, 'shallow copy has incorrect composed children');
  }
});

test('cloneNode deep', function() {
  var a = document.createElement('div');
  a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
  var b = a.cloneNode(true);
  document.body.appendChild(b);
  assert.equal(b.firstElementChild.childNodes.length, 2, 'deep copy has incorrect children');
  if (checkUnpatchedDom) {
    assert.equal(b.children.length, 4, 'deep copy has incorrect composed children');
  }
});

test('importNode shallow', function() {
  var a = document.createElement('div');
  a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
  // NOTE: Safari defaults do `deep` true for importNode so be explicit here.
  var b = document.importNode(a.firstElementChild, false);
  document.body.appendChild(b);
  assert.equal(b.childNodes.length, 0, 'shallow import has incorrect children');
  if (checkUnpatchedDom) {
    assert.equal(b.children.length, 2, 'shallow import has incorrect composed children');
  }
});

test('importNode deep', function() {
  var a = document.createElement('div');
  a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
  var b = document.importNode(a, true);
  document.body.appendChild(b);
  assert.equal(b.firstElementChild.childNodes.length, 2, 'deep copy has incorrect children');
  if (checkUnpatchedDom) {
    assert.equal(b.children.length, 4, 'deep copy has incorrect composed children');
  }
});

test('styling: flush causes attached and re-flushes if necessary', function(done) {
  var a = document.createElement('x-attach1');
  document.body.appendChild(a);
  flush();
  assert.equal(a.offsetHeight, 540);
  done();
});

test('flush reentrancy', function() {
  // Setup callbacks
  var order = [];
  var cb1 = sinon.spy(function() { order.push(cb1); });
  var cb2 = sinon.spy(function() { order.push(cb2); });
  var cb3 = sinon.spy(function() { order.push(cb3); });
  var cb4 = sinon.spy(function() { order.push(cb4); });
  var cbReentrant = sinon.spy(function() {
    order.push(cbReentrant);
    ShadyDOM.enqueue(cb3);
    flush();
    ShadyDOM.enqueue(cb4);
  });
  // Enqueue
  ShadyDOM.enqueue(cb1);
  ShadyDOM.enqueue(cbReentrant);
  ShadyDOM.enqueue(cb2);
  // Flush
  flush();
  // Check callbacks called and in correct order
  assert.isTrue(cb1.calledOnce);
  assert.isTrue(cb2.calledOnce);
  assert.isTrue(cb3.calledOnce);
  assert.isTrue(cb4.calledOnce);
  assert.isTrue(cbReentrant.calledOnce);
  assert.sameMembers(order, [cb1, cbReentrant, cb2, cb3, cb4]);
});

test('event.composedPath correctly calculated for elements with destination insertion points', function(done) {
  var re = document.createElement('x-reproject');
  document.body.appendChild(re);
  flush();
  var p = re.root.querySelector('x-project');
  var child = document.createElement('p');
  child.innerHTML = "hello";
  // child will be inserted into p after distributeContent is performed.
  re.appendChild(child);
  flush();
  child.addEventListener('child-event', function(e){
    var path = e.composedPath();
    assert.isTrue(path.indexOf(p) !== -1, 'path contains p');
    assert.isTrue(path.indexOf(re) !== -1, 'path contains re');
    done();
  });
  var evt = new CustomEvent('child-event');
  child.dispatchEvent(evt);
  document.body.removeChild(re);
});

test('initial distribution is synchronous when `syncInitialRender` is true', function() {
  if (!window.syncInitialRender) {
    this.skip();
  }
  const el = document.createElement('x-test');
  document.body.appendChild(el);
  let child = ShadyDOM.nativeTree.firstElementChild(el);
  assert.equal(child.localName, 'x-rereproject');
  child = ShadyDOM.nativeTree.firstElementChild(child);
  assert.equal(child.localName, 'x-reproject');
  child = ShadyDOM.nativeTree.firstElementChild(child);
  assert.equal(child.localName, 'x-project');
  assert.equal(ShadyDOM.nativeTree.textContent(child), 'x-project: [x-reproject: [x-rereproject: [projected]]]');
  document.body.removeChild(el);
});

});

suite('Accessors', function() {
  var noDistribute, distribute;

  suiteSetup(function() {
    noDistribute = document.querySelector('.accessors x-test-no-distribute');
    distribute = document.querySelector('.accessors x-project');
  });

  test('node accessors (no distribute)', function() {
    var child = noDistribute.children[0];
    assert.isTrue(child.classList.contains('child'), 'test node could not be found');
    var before = document.createElement('div');
    var after = document.createElement('div');
    noDistribute.insertBefore(before, child);
    noDistribute.appendChild(after);
    flush();
    assert.equal(noDistribute.firstChild, before, 'firstChild incorrect');
    assert.equal(noDistribute.lastChild, after, 'lastChild incorrect');
    assert.equal(before.nextSibling, child, 'nextSibling incorrect');
    assert.equal(child.nextSibling, after, 'nextSibling incorrect');
    assert.equal(after.previousSibling, child, 'previousSibling incorrect');
    assert.equal(after.nextSibling, null, 'nextSibling incorrect');
    assert.equal(child.previousSibling, before, 'previousSibling incorrect');
  });

  test('node accessors (distribute)', function() {
    var child = distribute.children[0];
    assert.isTrue(child.classList.contains('child'), 'test node could not be found');
    var before = document.createElement('div');
    var after = document.createElement('div');
    distribute.insertBefore(before, child);
    distribute.appendChild(after);
    flush();
    assert.equal(distribute.firstChild, before, 'firstChild incorrect');
    assert.equal(distribute.lastChild, after, 'lastChild incorrect');
    assert.equal(before.nextSibling, child, 'nextSibling incorrect');
    assert.equal(child.nextSibling, after, 'nextSibling incorrect');
    assert.equal(after.previousSibling, child, 'previousSibling incorrect');
    assert.equal(after.nextSibling, null, 'nextSibling incorrect');
    assert.equal(child.previousSibling, before, 'previousSibling incorrect');
  });

  test('element accessors (no distribute)', function() {
    var parent = document.createElement('x-test-no-distribute');
    var child = document.createElement('div');
    parent.appendChild(child);
    var before = document.createElement('div');
    var after = document.createElement('div');
    parent.insertBefore(before, child);
    parent.appendChild(after);
    assert.equal(parent.firstElementChild, before, 'firstElementChild incorrect');
    assert.equal(parent.lastElementChild, after, 'lastElementChild incorrect');
    assert.equal(before.nextElementSibling, child, 'nextElementSibling incorrect');
    assert.equal(child.nextElementSibling, after, 'nextElementSibling incorrect');
    assert.equal(after.previousElementSibling, child, 'previousElementSibling incorrect');
    assert.equal(child.previousElementSibling, before, 'previousElementSibling incorrect');
  });

  test('element accessors (distribute)', function() {
    var parent = document.createElement('x-project');
    var child = document.createElement('div');
    parent.appendChild(child);
    var before = document.createElement('div');
    var after = document.createElement('div');
    parent.insertBefore(before, child);
    parent.appendChild(after);
    assert.equal(parent.firstElementChild, before, 'firstElementChild incorrect');
    assert.equal(parent.lastElementChild, after, 'lastElementChild incorrect');
    assert.equal(before.nextElementSibling, child, 'nextElementSibling incorrect');
    assert.equal(child.nextElementSibling, after, 'nextElementSibling incorrect');
    assert.equal(after.previousElementSibling, child, 'previousElementSibling incorrect');
    assert.equal(child.previousElementSibling, before, 'previousElementSibling incorrect');
  });

  test('node accessors (empty logical tree)', function() {
    var element = document.createElement('x-simple');
    assert.equal(element.parentNode, null, 'parentNode incorrect');
    assert.equal(element.firstChild, null, 'firstChild incorrect');
    assert.equal(element.lastChild, null, 'lastChild incorrect');
    assert.equal(element.nextSibling, null, 'nextSibling incorrect');
    assert.equal(element.previousSibling, null, 'previousSibling incorrect');
    assert.equal(element.firstElementChild, null, 'firstElementChild incorrect');
    assert.equal(element.lastElementChild, null, 'lastElementChild incorrect');
    assert.equal(element.nextElementSibling, null, 'nextElementSibling incorrect');
    assert.equal(element.previousElementSibling, null, 'previousElementSibling incorrect');
  });

  test('node accessors (unmanaged logical tree)', function() {
    var element = document.createElement('div');
    var child1 = document.createElement('div');
    var child2 = document.createElement('div');
    element.appendChild(child1);
    element.appendChild(child2);
    assert.equal(element.parentNode, null, 'parentNode incorrect');
    assert.equal(element.firstChild, child1, 'firstChild incorrect');
    assert.equal(element.lastChild, child2, 'lastChild incorrect');
    assert.equal(element.nextSibling, null, 'nextSibling incorrect');
    assert.equal(element.previousSibling, null, 'previousSibling incorrect');
    assert.equal(element.firstElementChild, child1, 'firstElementChild incorrect');
    assert.equal(element.lastElementChild, child2, 'lastElementChild incorrect');
    assert.equal(element.nextElementSibling, null, 'nextElementSibling incorrect');
    assert.equal(element.previousElementSibling, null, 'previousElementSibling incorrect');
  });

  test('textContent', function() {
    var testElement = document.createElement('x-project');
    testElement.textContent = 'Hello World';
    assert.equal(testElement.textContent, 'Hello World', 'textContent getter incorrect');
    if (checkUnpatchedDom) {
      flush();
      assert.equal(Polymer.TreeApi.Composed.getChildNodes(testElement)[1].textContent, 'Hello World', 'text content setter incorrect');
    }
    testElement = createEnabledElement('x-commented');
    assert.equal(testElement.root.textContent, '[]', 'text content getter with comment incorrect');

    var textNode = document.createTextNode('foo');
    assert.equal(textNode.textContent, 'foo', 'text content getter on textnode incorrect');
    textNode.textContent = 'bar';
    assert.equal(textNode.textContent, 'bar', 'text content setter on textnode incorrect');

    var commentNode = document.createComment('foo');
    assert.equal(commentNode.textContent, 'foo', 'text content getter on commentnode incorrect');
    commentNode.textContent = 'bar';
    assert.equal(commentNode.textContent, 'bar', 'text content setter on commentnode incorrect');
  });

  test('innerHTML', function() {
    var testElement = document.createElement('x-project');
    testElement.innerHTML = '<div>Hello World</div><div>2</div><div>3</div>';
    var added = testElement.firstChild;
    assert.equal(added.textContent , 'Hello World', 'innerHTML setter incorrect');
    assert.equal(testElement.innerHTML , '<div>Hello World</div><div>2</div><div>3</div>', 'innerHTML getter incorrect');
    if (checkUnpatchedDom) {
      flush();
      var children = Polymer.TreeApi.Composed.getChildNodes(testElement);
      assert.equal(children[1], added, 'innerHTML setter composed incorrectly');
      assert.equal(children[2].textContent, '2', 'innerHTML setter composed incorrectly');
      assert.equal(children[3].textContent, '3', 'innerHTML setter composed incorrectly');
    }
  });

  test('innerHTML (non-composed)', function() {
    var testElement = document.createElement('div');
    document.body.appendChild(testElement);
    testElement.innerHTML = '<div>Hello World</div><div>2</div><div>3</div>';
    var added = testElement.firstChild;
    assert.equal(added.textContent , 'Hello World', 'innerHTML setter incorrect');
    assert.equal(testElement.innerHTML , '<div>Hello World</div><div>2</div><div>3</div>', 'innerHTML getter incorrect');
    assert.equal(testElement.children.length, 3);
  });

});

suite('activeElement', function() {
  var r;
    // light
      var r_l;
    // shadow
      var r_0;
        // light
          var r_0_l;
        // shadow
          var r_0_0;
            // light
              var r_0_0_l;
                // shadow
                  var r_0_0_l_0;
          var r_0_1;
            // light
              var r_0_1_l;
      var r_1;
        // light
          var r_1_l;
            // shadow
              var r_1_l_0;
        // shadow
          var r_1_0;
            // light
              var r_1_0_l;
          var r_1_1;
            // light
              var r_1_1_l;

  suiteSetup(function() {
    r = document.querySelector('x-shadow-host-root');
      r_l = r.querySelector('x-shadow-host-root-light');
      r_0 = r.root.querySelector('x-shadow-host-root-0');
        r_0_l = r_0.querySelector('x-shadow-host-root-0-light');
        r_0_0 = r_0.root.querySelector('x-shadow-host-root-0-0');
          r_0_0_l = r_0_0.querySelector('x-shadow-host-root-0-0-light');
            r_0_0_l_0 = r_0_0_l.root.querySelector('x-shadow-host-root-0-0-light-0');
        r_0_1 = r_0.root.querySelector('x-shadow-host-root-0-1');
          r_0_1_l = r_0_1.querySelector('x-shadow-host-root-0-1-light');
      r_1 = r.root.querySelector('x-shadow-host-root-1');
        r_1_l = r_1.querySelector('x-shadow-host-root-1-light');
          r_1_l_0 = r_1_l.root.querySelector('x-shadow-host-root-1-light-0');
        r_1_0 = r_1.root.querySelector('x-shadow-host-root-1-0');
          r_1_0_l = r_1_0.querySelector('x-shadow-host-root-1-0-light');
        r_1_1 = r_1.root.querySelector('x-shadow-host-root-1-1');
          r_1_1_l = r_1_1.querySelector('x-shadow-host-root-1-1-light');
  });

  test('r.focus()', function() {
    r.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, null, 'r.root.activeElement === null');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_l.focus()', function() {
    r_l.focus();

    assert.equal(document._activeElement, r_l, 'document.activeElement === r_l');
    assert.equal(r.root.activeElement, null, 'r.root.activeElement === null');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_0.focus()', function() {
    r_0.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_0, 'r.root.activeElement === r_0');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_0_l.focus()', function() {
    r_0_l.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_0_l, 'r.root.activeElement === r_0_l');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');

    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_0_0.focus()', function() {
    r_0_0.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_0, 'r.root.activeElement === r_0');
    assert.equal(r_0.root.activeElement, r_0_0, 'r_0.root.activeElement === r_0_0');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_0_0_l.focus()', function() {
    r_0_0_l.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_0, 'r.root.activeElement === r_0');
    assert.equal(r_0.root.activeElement, r_0_0_l, 'r_0.root.activeElement === r_0_0_l');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_0_0_l_0.focus()', function() {
    r_0_0_l_0.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_0, 'r.root.activeElement === r_0');
    assert.equal(r_0.root.activeElement, r_0_0_l, 'r_0.root.activeElement === r_0_0_l');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_0_l.root.activeElement, r_0_0_l_0, 'r_0_0_l.root.activeElement === r_0_0_l_0');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_0_1.focus()', function() {
    r_0_1.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_0, 'r.root.activeElement === r_0');
    assert.equal(r_0.root.activeElement, r_0_1, 'r_0.root.activeElement === r_0_1');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_0_1_l.focus()', function() {
    r_0_1_l.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_0, 'r.root.activeElement === r_0');
    assert.equal(r_0.root.activeElement, r_0_1_l, 'r_0.root.activeElement === r_0_1_l');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_1.focus()', function() {
    r_1.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1, 'r.root.activeElement === r_1');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_1_l.focus()', function() {
    r_1_l.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1_l, 'r.root.activeElement === r_1_l');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_1_l_0.focus()', function() {
    r_1_l_0.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1_l, 'r.root.activeElement === r_1_l');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, null, 'r_1.root.activeElement === null');
    assert.equal(r_1_l.root.activeElement, r_1_l_0, 'r_1.root.activeElement === r_1_l_0');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_1_0.focus()', function() {
    r_1_0.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1, 'r.root.activeElement === r_1');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, r_1_0, 'r_1.root.activeElement === r_1_0');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_1_0_l.focus()', function() {
    r_1_0_l.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1, 'r.root.activeElement === r_1');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, r_1_0_l, 'r_1.root.activeElement === r_1_0_l');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_1_1.focus()', function() {
    r_1_1.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1, 'r.root.activeElement === r_1');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, r_1_1, 'r_1.root.activeElement === r_1_1');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('r_1_1_l.focus()', function() {
    r_1_1_l.focus();

    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1, 'r.root.activeElement === r_1');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, r_1_1_l, 'r_1.root.activeElement === r_1_1_l');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('setting activeElement on document has no effect', function() {
    r_1_1.focus();

    // throws on some browsers only
    try {
      document.activeElement = "abc";
    } catch(e) {
      // no need to do anything
    }
    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1, 'r.root.activeElement === r_1');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, r_1_1, 'r_1.root.activeElement === r_1_1');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });

  test('setting activeElement on a shadow root has no effect', function() {
    r_1_1.focus();

    // throws on some browsers only
    try {
      r_1.root.activeElement = "abc";
    } catch(e) {
      // no need to do anything
    }
    assert.equal(document._activeElement, r, 'document.activeElement === r');
    assert.equal(r.root.activeElement, r_1, 'r.root.activeElement === r_1');
    assert.equal(r_0.root.activeElement, null, 'r_0.root.activeElement === null');
    assert.equal(r_0_0.root.activeElement, null, 'r_0_0.root.activeElement === null');
    assert.equal(r_0_1.root.activeElement, null, 'r_0_1.root.activeElement === null');
    assert.equal(r_1.root.activeElement, r_1_1, 'r_1.root.activeElement === r_1_1');
    assert.equal(r_1_0.root.activeElement, null, 'r_1_0.root.activeElement === null');
    assert.equal(r_1_1.root.activeElement, null, 'r_1_1.root.activeElement === null');
  });
});

suite('non-distributed elements', function() {

  var nd;

  suiteSetup(function() {
    nd = document.querySelector('#noDistribute');
  });

  test('finds undistributed child', function() {
    assert.ok(nd.children.length, 2, 'light children includes distributed and non-distributed nodes');
  });

  test('removes/adds undistributed child', function() {
    var b = nd.children[0];
    assert.equal(allInsertionPoints(b).length, 0, 'element improperly distributed');
    nd.removeChild(b);
    flush();
    assert.equal(nd.children.length, 1, 'children length not decremented due to element removal');
    nd.appendChild(b);
    flush();
    assert.equal(nd.children.length, 2, 'children length not incremented due to element addition');
    var d = document.createElement('div');
    d.innerHTML = 'added';
    nd.insertBefore(d, b);
    flush();
    assert.equal(nd.children.length, 3, 'children length not incremented due to element addition');
    nd.removeChild(d);
    flush();
    assert.equal(nd.children.length, 2, 'children length not decremented due to element removal');
  });

  test('removes/adds between light and local dom', function() {
    var b = nd.children[1];
    assert.equal(allInsertionPoints(b).length, 0, 'element improperly distributed');
    nd.root.appendChild(b);
    flush();
    assert.equal(nd.children.length, 1, 'children length not decremented due to element removal');
    assert.equal(nd.root.children.length, 2, 'root children length not incremented due to element addition');
    nd.appendChild(b);
    flush();
    assert.equal(nd.children.length, 2, 'children length not incremented due to element addition');
    assert.equal(nd.root.children.length, 1, 'root children length not decremented due to element removal');
  });

  test('modifying slots correctly distributes changes to light dom', function() {
    function testNoAttr() {
      assert.equal(allInsertionPoints(child)[0], d.$.notTestContent, 'child not distributed logically');
      if (checkUnpatchedDom) {
        assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.notTestContainer, 'child not rendered in composed dom');
      }
    }
    function testWithAttr() {
      assert.equal(allInsertionPoints(child)[0], d.$.testContent, 'child not distributed logically');
      if (checkUnpatchedDom) {
        assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.testContainer, 'child not rendered in composed dom');
      }
    }
    // test with x-distribute
    var d = document.createElement('x-distribute');
    document.body.appendChild(d);
    var child = document.createElement('div');
    child.classList.add('child');
    child.textContent = 'Child';
    d.appendChild(child);
    flush();
    assert.equal(d.children[0], child, 'child not added to logical dom');
    testNoAttr();
    // set / unset `test` attr and see if it distributes properly
    child.setAttribute('slot', 'test');
    flush();
    testWithAttr();
    //
    child.removeAttribute('slot');
    flush();
    testNoAttr();
    //
    child.setAttribute('slot', 'test');
    flush();
    testWithAttr();
  });

  test('getRootNode', function() {
    var test = document.createElement('div');
    var c1 = createEnabledElement('x-compose');
    var c2 = createEnabledElement('x-compose');
    c1.$.project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
    c2.$.project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c2.root, 'getOwnerRoot not correctly reset when element moved to different root');
    c1.appendChild(test);
    assert.equal(test.getRootNode(), c1, 'getOwnerRoot incorrect for child moved from a root to no root');
  });

  test('getRootNode when out of tree', function() {
    var test = document.createElement('div');
    assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect when not in root');
    var c1 = createEnabledElement('x-compose');
    var project = c1.$.project;
    project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
    project.removeChild(test);
    flush();
    assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root');
    project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
  });

  test('getRootNode when out of tree and adding subtree', function() {
    var container = document.createDocumentFragment();
    var test = document.createElement('div');
    container.appendChild(test);
    assert.equal(test.getRootNode(), container, 'getOwnerRoot incorrect when not in root');
    var c1 = createEnabledElement('x-compose');
    var project = c1.$.project;
    project.appendChild(container);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
    project.removeChild(test);
    flush();
    assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root');
    project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
  });

  test('getRootNode, subtree', function() {
    var test = document.createElement('div');
    var testChild = document.createElement('div');
    test.appendChild(testChild);
    assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect when not in root');
    var c1 = createEnabledElement('x-compose');
    var project = c1.$.project;
    project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
    assert.equal(testChild.getRootNode(), c1.root, 'getOwnerRoot incorrect for sub-child added to element in root');
    project.removeChild(test);
    flush();
    assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root');
    assert.equal(testChild.getRootNode(), test, 'getOwnerRoot incorrect for sub-child moved from a root to no root');
    project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
    assert.equal(testChild.getRootNode(), c1.root, 'getOwnerRoot incorrect for sub-child added to element in root');
  });

  test('getRootNode (paper-ripple use case)', function() {
    var test = document.createElement('div');
    // child
    var d = document.createElement('div');
    test.appendChild(d);
    var c1 = createEnabledElement('x-compose');
    var c2 = createEnabledElement('x-compose');
    c1.$.project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root');
    c2.$.project.appendChild(test);
    flush();
    assert.equal(test.getRootNode(), c2.root, 'getOwnerRoot not correctly reset when element moved to different root');
    c1.appendChild(test);
    assert.equal(test.getRootNode(), c1, 'getOwnerRoot incorrect for child moved from a root to no root');
  });

  test('getDestinationInsertionPoints on non-distributable element', function() {
    var d = document.createElement('div');
    assert.equal(allInsertionPoints(d).length, 0);
    assert.equal(allInsertionPoints(document).length, 0);
  });

  test('(Polymer.dom) Deep Contains', function() {
    var el = document.querySelector('x-deep-contains');
    var shadow = el.$.shadowed;
    var light = el.querySelector('[slot=light]');
    var notdistributed = el.children[1];
    var disconnected = document.createElement('div');
    var separate = document.createElement('div');
    document.body.appendChild(separate);

    var pd = dom(el);

    assert.equal(pd.deepContains(el), true, 'Element should deepContain itself');
    assert.equal(pd.deepContains(shadow), true, 'Shadowed Child element should be found');
    assert.equal(pd.deepContains(light), true, 'Light Child element should be found');
    assert.equal(pd.deepContains(notdistributed), true, 'Non-distributed child element should be found');
    assert.equal(pd.deepContains(disconnected), false, 'Disconnected element should not be found');
    assert.equal(pd.deepContains(separate), false, 'Unassociated, attached element should not be found');

    document.body.removeChild(separate);
  });

});
</script>

</body>
</html>
